package itb.fhv.sa.pipesfilters;


public class LineFilter extends Filter implements Runnable {

	public static final int LEFT_ALIGNED = 0;
	public static final int CENTER_ALIGNED = 1;
	public static final int RIGHT_ALIGNED = 2;

	private int m_lineLength;
	private int m_alignment;

	private StringBuffer m_line;


	public LineFilter(Pipe next, int lineLength, int alignment) {
		super(next);
		m_neighbour1 = next;
		m_lineLength = lineLength;
		m_alignment = alignment;
		m_line = new StringBuffer();
	}


	/**
	 * Reads Word objects and appends them to the current Line object, until the
	 * line is full. This method attempts to split the last Word of a line, if
	 * the Word causes the line length to be exceeded. If the first part of the split Word
	 * cannot fit, then the Word is used to start a new line, and the
	 * current line is returned to the caller.
	 * 
	 * @return A Line object created from the Word objects.
	 */
	@Override
	public PLOutput read() {
		boolean split = false;
		String[] wordParts = { null, null };
		Line line = null;
		Word word = (Word) m_neighbour1.read();

		// read word until the line is full or the data ends
		while (word != null
				&& word.getContent().length() + m_line.length() <= m_lineLength
				&& !word.getContent().endsWith("\1 ")) {
			m_line.append(word.getContent());
			word = (Word) m_neighbour1.read();
		}

		if (word != null) {
			// try to split word
			if (word.getContent().length() + m_line.length() > m_lineLength) {
				wordParts = (word.splitWord());

				if (wordParts[0] != null) {

					// make sure first part of split word fits in line
					if (wordParts[0].length() <= (m_lineLength - m_line
							.length())) {
						m_line.append(wordParts[0]);
						split = true;
					}

				}

				if (wordParts[1] != null && split == true) {
					m_line.append(wordParts[1]);
				} else {
					m_line.append(word.getContent() + " ");
				}

				endLine();
			}

			if (word.getContent().endsWith("\1 ")) {
				m_line.append(word.getContent());

				// remove control character
				m_line.delete(m_line.lastIndexOf("\1 "), m_line.length() - 1);

				endLine();

				// re-append control character (without space)
				m_line.append("\1");
			}

			line = new Line(new String(m_line));
			m_line = new StringBuffer();
		}

		return line;
	}


	/**
	 * Appends the provided Word to the current line. If the Word cannot fit in
	 * the current line, this method attempts to split the word. If the first
	 * part of the split word still cannot fit, then the Word is used to start a
	 * new line, and the current line is pushed to the next pipeline element.
	 * 
	 * @param plo
	 *            The Word object to be appended to this line.
	 */
	@Override
	public boolean write(PLOutput plo) {
		boolean split = false;
		boolean written = false;
		String[] wordParts = { null, null };

		if (((Word) plo).getContent().length() > (m_lineLength - m_line
				.length())) {
			// if word is too long, start a new line
			wordParts = ((Word) plo).splitWord();

			if (wordParts[0] != null) {

				if (wordParts[0].length() <= (m_lineLength - m_line.length())) {
					m_line.append(wordParts[0]);
					split = true;
				}

			}

			endLine();

			// create the Line object
			Line line = new Line(new String(m_line));

			// write to next PL element
			while (written == false) {
				written = m_neighbour1.write(line);
			}

			written = false;
			m_line = new StringBuffer();
		}

		if (wordParts[1] != null && split == true) {
			m_line.append(wordParts[1]);
		} else {
			m_line.append(((Word) plo).getContent() + " ");
		}

		return false;
	}


	/**
	 * Ends the current line according to the specified alignment.
	 * This method inserts spaces until the line length is reached.
	 */
	private void endLine() {
		// remove the last character (space)

		if (m_line.length() > 0) {
			m_line.deleteCharAt(m_line.length() - 1);
		}

		if (m_alignment == RIGHT_ALIGNED) {

			// fill line with blank spaces
			while (m_line.length() < m_lineLength) {
				m_line.insert(0, ' ');
			}

		} else if (m_alignment == CENTER_ALIGNED) {

			// fill line with blank spaces
			while (m_line.length() < m_lineLength - 1) {
				m_line.insert(0, ' ');
				m_line.append(' ');
			}

		}

	}


	public void setAlignment(int alignment) {
		m_alignment = alignment;
	}


	public void setLineLength(int lineLength) {
		m_lineLength = lineLength;
	}


	/**
	 * Method to flush push pipelines.
	 */
	@Override
	public void flush() {
		boolean written = false;
		Line line = new Line(new String(m_line));

		while (written == false) {
			written = m_neighbour1.write(line);
		}

		// propagate the end of the data
		endLine();
		m_line = new StringBuffer();
		m_neighbour1.flush();
	}

}
